//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_entry_dao

import (
	"context"
	"regexp"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_entry_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type EntryPermDbItem struct {
	UpdateForAll          bool     `bson:"updateForAll"`
	ExtraUpdateUserIdList []string `bson:"extraUpdateUserIdList"`
}

type ExtraSpritInfoDbItem struct {
	StartTime         int64   `bson:"startTime"`
	EndTime           int64   `bson:"endTime"`
	NonWorkDayList    []int64 `bson:"nonWorkDayList"`
	IssueListType     uint32  `bson:"issueListType"`
	HideGanttPanel    bool    `bson:"hideGanttPanel"`
	HideBurndownPanel bool    `bson:"hideBurndownPanel"`
	HideStatPanel     bool    `bson:"hideStatPanel"`
	HideSummaryPanel  bool    `bson:"hideSummaryPanel"`
	HideTestPlanPanel bool    `bson:"hideTestPlanPanel"`
}

type ExtraPagesInfoDbItem struct {
	FileId string `bson:"fileId"`
}

type ExtraApiCollInfoDbItem struct {
	ApiCollType uint32 `bson:"apiCollType"`
	DefaultAddr string `bson:"defaultAddr"`
}

type ExtraDataAnnoInfoDbItem struct {
	AnnoType uint32 `bson:"annoType"`
}

type EntryInfoDbItem struct {
	EntryId           string                   `bson:"_id"`
	ProjectId         string                   `bson:"projectId"`
	EntryType         uint32                   `bson:"entryType"`
	EntryTitle        string                   `bson:"entryTitle"`
	TagIdList         []string                 `bson:"tagIdList"`
	EntryPerm         EntryPermDbItem          `bson:"entryPerm"`
	ExtraSpritInfo    *ExtraSpritInfoDbItem    `bson:"extraSpritInfo"`
	ExtraPagesInfo    *ExtraPagesInfoDbItem    `bson:"extraPagesInfo"`
	ExtraApiCollInfo  *ExtraApiCollInfoDbItem  `bson:"extraApiCollInfo"`
	ExtraDataAnnoInfo *ExtraDataAnnoInfoDbItem `bson:"extraDataAnnoInfo"`
	CreateUserId      string                   `bson:"createUserId"`
	CreateTime        int64                    `bson:"createTime"`
	UpdateUserId      string                   `bson:"updateUserId"`
	UpdateTime        int64                    `bson:"updateTime"`
}

func (item *EntryInfoDbItem) ToEntryInfo(tagList []*project_entry_api.EntryTag) *project_entry_api.EntryInfo {
	retItem := &project_entry_api.EntryInfo{
		EntryId:    item.EntryId,
		EntryType:  project_entry_api.ENTRY_TYPE(item.EntryType),
		EntryTitle: item.EntryTitle,
		TagList:    tagList,
		EntryPerm: &project_entry_api.EntryPerm{
			UpdateForAll:          item.EntryPerm.UpdateForAll,
			ExtraUpdateUserIdList: item.EntryPerm.ExtraUpdateUserIdList,
		},
		CreateUserId:  item.CreateUserId,
		CreateTime:    item.CreateTime,
		UpdateUserId:  item.UpdateUserId,
		UpdateTime:    item.UpdateTime,
		MyWatch:       false,
		WatchUserList: []*project_entry_api.WatchUser{},
	}
	if item.EntryType == uint32(project_entry_api.ENTRY_TYPE_ENTRY_TYPE_SPRIT) {
		retItem.ExtraInfo = &project_entry_api.EntryInfo_ExtraSpritInfo{
			ExtraSpritInfo: &project_entry_api.ExtraSpritInfo{
				StartTime:         item.ExtraSpritInfo.StartTime,
				EndTime:           item.ExtraSpritInfo.EndTime,
				NonWorkDayList:    item.ExtraSpritInfo.NonWorkDayList,
				IssueListType:     project_entry_api.ISSUE_LIST_TYPE(item.ExtraSpritInfo.IssueListType),
				HideGanttPanel:    item.ExtraSpritInfo.HideGanttPanel,
				HideBurndownPanel: item.ExtraSpritInfo.HideBurndownPanel,
				HideStatPanel:     item.ExtraSpritInfo.HideStatPanel,
				HideSummaryPanel:  item.ExtraSpritInfo.HideSummaryPanel,
				HideTestPlanPanel: item.ExtraSpritInfo.HideTestPlanPanel,
			},
		}
	} else if item.EntryType == uint32(project_entry_api.ENTRY_TYPE_ENTRY_TYPE_PAGES) {
		retItem.ExtraInfo = &project_entry_api.EntryInfo_ExtraPagesInfo{
			ExtraPagesInfo: &project_entry_api.ExtraPagesInfo{
				FileId: item.ExtraPagesInfo.FileId,
			},
		}
	} else if item.EntryType == uint32(project_entry_api.ENTRY_TYPE_ENTRY_TYPE_API_COLL) {
		retItem.ExtraInfo = &project_entry_api.EntryInfo_ExtraApiCollInfo{
			ExtraApiCollInfo: &project_entry_api.ExtraApiCollInfo{
				ApiCollType: project_entry_api.API_COLL_TYPE(item.ExtraApiCollInfo.ApiCollType),
				DefaultAddr: item.ExtraApiCollInfo.DefaultAddr,
			},
		}
	}
	return retItem
}

type _EntryDao struct {
	mc *mongo.Collection
}

func newEntryDao(db *mongo.Database) (*_EntryDao, error) {
	mc := db.Collection(db_ns.DB_NS_PROJECT_ENTRY + "entry")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys: bson.D{{"projectId", 1}, {"createTime", -1}},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_EntryDao{mc: mc}, nil
}

func (db *_EntryDao) Insert(ctx context.Context, dbItem *EntryInfoDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_EntryDao) Exist(ctx context.Context, entryId, projectId string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": entryId, "projectId": projectId})
	return count > 0, err
}

func (db *_EntryDao) Get(ctx context.Context, entryId, projectId string) (*EntryInfoDbItem, error) {
	retItem := &EntryInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": entryId, "projectId": projectId}).Decode(retItem)
	return retItem, err
}

func (db *_EntryDao) Count(ctx context.Context, projectId string, param *project_entry_api.ListParam, watchEntryIdList []string) (uint32, error) {
	query := db.genListQuery(projectId, param, watchEntryIdList)
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_EntryDao) List(ctx context.Context, projectId string, param *project_entry_api.ListParam, watchEntryIdList []string, offset, limit int64) ([]*EntryInfoDbItem, error) {
	query := db.genListQuery(projectId, param, watchEntryIdList)
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"createTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*EntryInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_EntryDao) ListById(ctx context.Context, entryIdList []string) ([]*EntryInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": entryIdList}})
	if err != nil {
		return nil, err
	}
	retList := []*EntryInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_EntryDao) DistinctEntryIdByProject(ctx context.Context, projectId string, entryType project_entry_api.ENTRY_TYPE) ([]string, error) {
	tmpList, err := db.mc.Distinct(ctx, "_id", bson.M{"projectId": projectId, "entryType": uint32(entryType)})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpValue := range tmpList {
		if tmpValue.(string) != "" {
			retList = append(retList, tmpValue.(string))
		}
	}
	return retList, nil
}

func (db *_EntryDao) genListQuery(projectId string, param *project_entry_api.ListParam, watchEntryIdList []string) bson.M {
	query := bson.M{
		"projectId": projectId,
	}
	if param.FilterByWatch {
		query["_id"] = bson.M{"$in": watchEntryIdList}
	}
	if param.FilterByKeyword {
		query["entryTitle"] = primitive.Regex{
			Pattern: regexp.QuoteMeta(param.Keyword),
			Options: "i",
		}
	}
	if param.FilterByTagId {
		query["tagIdList"] = bson.M{"$in": param.TagIdList}
	}
	if param.FilterByEntryType {
		query["entryType"] = bson.M{"$in": param.EntryTypeList}
	}
	return query
}

func (db *_EntryDao) UpdateTag(ctx context.Context, entryId, projectId string, tagIdList []string, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": entryId, "projectId": projectId}, bson.M{"$set": bson.M{
		"tagIdList":    tagIdList,
		"updateUserId": updateUserId,
		"updateTime":   updateTime,
	}})
	return err
}

func (db *_EntryDao) UpdateTitle(ctx context.Context, entryId, projectId, entryTitle string, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": entryId, "projectId": projectId}, bson.M{"$set": bson.M{
		"entryTitle":   entryTitle,
		"updateUserId": updateUserId,
		"updateTime":   updateTime,
	}})
	return err
}

func (db *_EntryDao) UpdatePerm(ctx context.Context, entryId, projectId string, entryPerm *EntryPermDbItem, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": entryId, "projectId": projectId}, bson.M{"$set": bson.M{
		"entryPerm":    entryPerm,
		"updateUserId": updateUserId,
		"updateTime":   updateTime,
	}})
	return err
}

func (db *_EntryDao) UpdateExtraSpritInfo(ctx context.Context, entryId, projectId string, extraInfo *ExtraSpritInfoDbItem, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": entryId, "projectId": projectId}, bson.M{"$set": bson.M{
		"extraSpritInfo": extraInfo,
		"updateUserId":   updateUserId,
		"updateTime":     updateTime,
	}})
	return err
}

func (db *_EntryDao) UpdateExtraPagesInfo(ctx context.Context, entryId, projectId string, extraInfo *ExtraPagesInfoDbItem, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": entryId, "projectId": projectId}, bson.M{"$set": bson.M{
		"extraPagesInfo": extraInfo,
		"updateUserId":   updateUserId,
		"updateTime":     updateTime,
	}})
	return err
}

func (db *_EntryDao) UpdateExtraApiCollInfo(ctx context.Context, entryId, projectId string, extraInfo *ExtraApiCollInfoDbItem, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": entryId, "projectId": projectId}, bson.M{"$set": bson.M{
		"extraApiCollInfo": extraInfo,
		"updateUserId":     updateUserId,
		"updateTime":       updateTime,
	}})
	return err
}

func (db *_EntryDao) SetUpdateInfo(ctx context.Context, entryId, projectId, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": entryId, "projectId": projectId}, bson.M{"$set": bson.M{"updateUserId": updateUserId, "updateTime": updateTime}})
	return err
}

func (db *_EntryDao) Remove(ctx context.Context, entryId, projectId string) error {
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": entryId, "projectId": projectId})
	return err
}

// 清除所有的board入口
// ENTRY_TYPE_BOARD = 3;
func (db *_EntryDao) RemoveAllBoard(ctx context.Context) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"entryType": 3})
	return err
}

// 清除数据标注所有入口
// ENTRY_TYPE_DATA_ANNO = 6;
func (db *_EntryDao) RemoveAllDataAnno(ctx context.Context) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"entryType": 6})
	return err
}

// 清除文件所有入口
// ENTRY_TYPE_FILE = 4;
func (db *_EntryDao) RemoveAllFile(ctx context.Context) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"entryType": 4})
	return err
}
